$.extend($.fn.datagrid.defaults, {
    rowHeight: 25,
    maxDivHeight: 10000000,
    maxVisibleHeight: 15000000,
    deltaTopHeight: 0,
    onBeforeFetch: function (page) {
    },
    onFetch: function (page, rows) {
    },
    loader: function (param, success, error) {
        var opts = $(this).datagrid('options');
        if (!opts.url) return false;
        if (opts.view.type == 'scrollview') {
            param.page = param.page || 1;
            param.rows = param.rows || opts.pageSize;
        }
        $.ajax({
            type: opts.method,
            url: opts.url,
            data: param,
            dataType: 'json',
            success: function (data) {
                if (data.type == '302') {
                    $.messager.alert('提示', '对不起，你没有访问权限！');
                    return;
                }
                success(data);
            },
            error: function () {
                error.apply(this, arguments);
            }
        });
    }
});
$.extend($.fn.datagrid.defaults.finder, {
    getRow: function (target, p) {	// p can be row index or tr object
        var index = (typeof p == 'object') ? p.attr('datagrid-row-index') : p;
        var opts = $(target).datagrid('options');
        if (opts.view.type == 'scrollview') {
            index -= opts.view.index;
        }
        return $.data(target, 'datagrid').data.rows[index];
    }
});

var scrollview = $.extend({}, $.fn.datagrid.defaults.view, {
    type: 'scrollview',
    index: 0,
    r1: [],
    r2: [],
    rows: [],
    render: function (target, container, frozen) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var rows = this.rows || [];
        if (!rows.length) {
            return;
        }
        var fields = $(target).datagrid('getColumnFields', frozen);

        if (frozen) {
            if (!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {
                return;
            }
        }

        var index = this.index;
        var table = ['<div class="datagrid-btable-top"></div>',
            '<table class="datagrid-btable" cellspacing="0" cellpadding="0" border="0"><tbody>'];
        for (var i = 0; i < rows.length; i++) {
            var css = opts.rowStyler ? opts.rowStyler.call(target, index, rows[i]) : '';
            var classValue = '';
            var styleValue = '';
            if (typeof css == 'string') {
                styleValue = css;
            } else if (css) {
                classValue = css['class'] || '';
                styleValue = css['style'] || '';
            }
            var cls = 'class="datagrid-row ' + (index % 2 && opts.striped ? 'datagrid-row-alt ' : ' ') + classValue + '"';
            var style = styleValue ? 'style="' + styleValue + '"' : '';
            var rowId = state.rowIdPrefix + '-' + (frozen ? 1 : 2) + '-' + index;
            table.push('<tr id="' + rowId + '" datagrid-row-index="' + index + '" ' + cls + ' ' + style + '>');
            table.push(this.renderRow.call(this, target, fields, frozen, index, rows[i]));
            table.push('</tr>');

            // render the detail row
            if (opts.detailFormatter) {
                table.push('<tr style="display:none;">');
                if (frozen) {
                    table.push('<td colspan=' + (fields.length + (opts.rownumbers ? 1 : 0)) + ' style="border-right:0">');
                } else {
                    table.push('<td colspan=' + (fields.length) + '>');
                }
                table.push('<div class="datagrid-row-detail">');
                if (frozen) {
                    table.push('&nbsp;');
                } else {
                    table.push(opts.detailFormatter.call(target, index, rows[i]));
                }
                table.push('</div>');
                table.push('</td>');
                table.push('</tr>');
            }

            index++;
        }
        table.push('</tbody></table>');
        table.push('<div class="datagrid-btable-bottom"></div>');

        $(container).html(table.join(''));
    },

    renderRow: function (target, fields, frozen, rowIndex, rowData) {
        var opts = $.data(target, 'datagrid').options;

        var cc = [];
        if (frozen && opts.rownumbers) {
            var rownumber = rowIndex + 1;
            // if (opts.pagination){
            // 	rownumber += (opts.pageNumber-1)*opts.pageSize;
            // }
            cc.push('<td class="datagrid-td-rownumber"><div class="datagrid-cell-rownumber">' + rownumber + '</div></td>');
        }
        for (var i = 0; i < fields.length; i++) {
            var field = fields[i];
            var col = $(target).datagrid('getColumnOption', field);
            if (col) {
                var value = rowData[field];	// the field value
                var css = col.styler ? (col.styler(value, rowData, rowIndex) || '') : '';
                var classValue = '';
                var styleValue = '';
                if (typeof css == 'string') {
                    styleValue = css;
                } else if (cc) {
                    classValue = css['class'] || '';
                    styleValue = css['style'] || '';
                }
                var cls = classValue ? 'class="' + classValue + '"' : '';
                var style = col.hidden ? 'style="display:none;' + styleValue + '"' : (styleValue ? 'style="' + styleValue + '"' : '');

                cc.push('<td field="' + field + '" ' + cls + ' ' + style + '>');

                if (col.checkbox) {
                    style = '';
                } else if (col.expander) {
                    style = "text-align:center;height:16px;";
                } else {
                    style = styleValue;
                    if (col.align) {
                        style += ';text-align:' + col.align + ';'
                    }
                    if (!opts.nowrap) {
                        style += ';white-space:normal;height:auto;';
                    } else if (opts.autoRowHeight) {
                        style += ';height:auto;';
                    }
                }

                cc.push('<div style="' + style + '" ');
                if (col.checkbox) {
                    cc.push('class="datagrid-cell-check ');
                } else {
                    cc.push('class="datagrid-cell ' + col.cellClass);
                }
                cc.push('">');

                if (col.checkbox) {
                    cc.push('<input type="checkbox" name="' + field + '" value="' + (value != undefined ? value : '') + '">');
                } else if (col.expander) {
                    //cc.push('<div style="text-align:center;width:16px;height:16px;">');
                    cc.push('<span class="datagrid-row-expander datagrid-row-expand" style="display:inline-block;width:16px;height:16px;cursor:pointer;" />');
                    //cc.push('</div>');
                } else if (col.formatter) {
                    cc.push(col.formatter(value, rowData, rowIndex));
                } else {
                    cc.push(value);
                }

                cc.push('</div>');
                cc.push('</td>');
            }
        }
        return cc.join('');
    },

    bindEvents: function (target) {
        var state = $.data(target, 'datagrid');
        var dc = state.dc;
        var opts = state.options;
        var body = dc.body1.add(dc.body2);
        var clickHandler = ($.data(body[0], 'events') || $._data(body[0], 'events')).click[0].handler;
        body.unbind('click').bind('click', function (e) {
            var tt = $(e.target);
            var tr = tt.closest('tr.datagrid-row');
            if (!tr.length) {
                return
            }
            if (tt.hasClass('datagrid-row-expander')) {
                var rowIndex = parseInt(tr.attr('datagrid-row-index'));
                if (tt.hasClass('datagrid-row-expand')) {
                    $(target).datagrid('expandRow', rowIndex);
                } else {
                    $(target).datagrid('collapseRow', rowIndex);
                }
                $(target).datagrid('fixRowHeight');

            } else {
                clickHandler(e);
            }
            e.stopPropagation();
        });
    },

    onBeforeRender: function (target) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var dc = state.dc;
        var view = this;

        state.data.firstRows = state.data.rows;
        state.data.rows = [];

        dc.body1.add(dc.body2).empty();
        this.rows = [];	// the rows to be rendered
        this.r1 = this.r2 = [];	// the first part and last part of rows

        init();
        createHeaderExpander();

        function init() {
            var pager = $(target).datagrid('getPager');
            pager.pagination({
                onSelectPage: function (pageNum, pageSize) {
                    opts.pageNumber = pageNum || 1;
                    opts.pageSize = pageSize;
                    pager.pagination('refresh', {
                        pageNumber: pageNum,
                        pageSize: pageSize
                    });
                    $(target).datagrid('gotoPage', opts.pageNumber);
                }
            });
            // erase the onLoadSuccess event, make sure it can't be triggered
            state.onLoadSuccess = opts.onLoadSuccess;
            opts.onLoadSuccess = function () {
            };
            if (!opts.remoteSort) {
                var onBeforeSortColumn = opts.onBeforeSortColumn;
                opts.onBeforeSortColumn = function (name, order) {
                    var result = onBeforeSortColumn.call(this, name, order);
                    if (result == false) {
                        return false;
                    }
                    state.data.rows = state.data.firstRows;
                }
            }
            dc.body2.unbind('.datagrid');
            setTimeout(function () {
                dc.body2.unbind('.datagrid').bind('scroll.datagrid', function (e) {
                    if (state.onLoadSuccess) {
                        opts.onLoadSuccess = state.onLoadSuccess;	// restore the onLoadSuccess event
                        state.onLoadSuccess = undefined;
                    }
                    if (view.scrollTimer) {
                        clearTimeout(view.scrollTimer);
                    }
                    view.scrollTimer = setTimeout(function () {
                        view.scrolling.call(view, target);
                    }, 50);
                });
                dc.body2.triggerHandler('scroll.datagrid');
            }, 0);
        }

        function createHeaderExpander() {
            if (!opts.detailFormatter) {
                return
            }

            var t = $(target);
            var hasExpander = false;
            var fields = t.datagrid('getColumnFields', true).concat(t.datagrid('getColumnFields'));
            for (var i = 0; i < fields.length; i++) {
                var col = t.datagrid('getColumnOption', fields[i]);
                if (col.expander) {
                    hasExpander = true;
                    break;
                }
            }
            if (!hasExpander) {
                if (opts.frozenColumns && opts.frozenColumns.length) {
                    opts.frozenColumns[0].splice(0, 0, {
                        field: '_expander',
                        expander: true,
                        width: 24,
                        resizable: false,
                        fixed: true
                    });
                } else {
                    opts.frozenColumns = [[{
                        field: '_expander',
                        expander: true,
                        width: 24,
                        resizable: false,
                        fixed: true
                    }]];
                }

                var t = dc.view1.children('div.datagrid-header').find('table');
                var td = $('<td rowspan="' + opts.frozenColumns.length + '"><div class="datagrid-header-expander" style="width:24px;"></div></td>');
                if ($('tr', t).length == 0) {
                    td.wrap('<tr></tr>').parent().appendTo($('tbody', t));
                } else if (opts.rownumbers) {
                    td.insertAfter(t.find('td:has(div.datagrid-header-rownumber)'));
                } else {
                    td.prependTo(t.find('tr:first'));
                }
            }

            setTimeout(function () {
                view.bindEvents(target);
            }, 0);
        }
    },

    onAfterRender: function (target) {
        $.fn.datagrid.defaults.view.onAfterRender.call(this, target);
        var dc = $.data(target, 'datagrid').dc;
        var footer = dc.footer1.add(dc.footer2);
        footer.find('span.datagrid-row-expander').css('visibility', 'hidden');
    },

    scrolling: function (target) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var dc = state.dc;
        if (!opts.finder.getRows(target).length) {
            this.reload.call(this, target);
        } else {
            if (!dc.body2.is(':visible')) {
                return
            }
            var headerHeight = dc.view2.children('div.datagrid-header').outerHeight();

            var topDiv = dc.body2.children('div.datagrid-btable-top');
            var bottomDiv = dc.body2.children('div.datagrid-btable-bottom');
            if (!topDiv.length || !bottomDiv.length) {
                return;
            }
            var top = topDiv.position().top + topDiv._outerHeight() - headerHeight;
            var bottom = bottomDiv.position().top - headerHeight;
            top = Math.floor(top);
            bottom = Math.floor(bottom);

            if (top > dc.body2.height() || bottom < 0) {
                this.reload.call(this, target);
            } else if (top > 0) {
                var page = Math.floor(this.index / opts.pageSize);
                this.getRows.call(this, target, page, function (rows) {
                    this.page = page;
                    this.r2 = this.r1;
                    this.r1 = rows;
                    this.index = (page - 1) * opts.pageSize;
                    this.rows = this.r1.concat(this.r2);
                    this.populate.call(this, target);
                });
            } else if (bottom < dc.body2.height()) {
                if (state.data.rows.length + this.index >= state.data.total) {
                    return;
                }
                var page = Math.floor(this.index / opts.pageSize) + 2;
                if (this.r2.length) {
                    page++;
                }
                this.getRows.call(this, target, page, function (rows) {
                    this.page = page;
                    if (!this.r2.length) {
                        this.r2 = rows;
                    } else {
                        this.r1 = this.r2;
                        this.r2 = rows;
                        this.index += opts.pageSize;
                    }
                    this.rows = this.r1.concat(this.r2);
                    this.populate.call(this, target);
                });
            }
        }
    },
    reload: function (target) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var dc = state.dc;
        var top = $(dc.body2).scrollTop() + opts.deltaTopHeight;
        var index = Math.floor(top / opts.rowHeight);
        var page = Math.floor(index / opts.pageSize) + 1;

        this.getRows.call(this, target, page, function (rows) {
            this.page = page;
            this.index = (page - 1) * opts.pageSize;
            this.rows = rows;
            this.r1 = rows;
            this.r2 = [];
            this.populate.call(this, target);
            dc.body2.triggerHandler('scroll.datagrid');
        });
    },

    getRows: function (target, page, callback) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var index = (page - 1) * opts.pageSize;

        if (index < 0) {
            return
        }
        if (opts.onBeforeFetch.call(target, page) == false) {
            return;
        }

        var rows = state.data.firstRows.slice(index, index + opts.pageSize);
        if (rows.length && (rows.length == opts.pageSize || index + rows.length == state.data.total)) {
            opts.onFetch.call(target, page, rows);
            callback.call(this, rows);
        } else {
            var param = $.extend({}, opts.queryParams, {
                page: page,
                rows: opts.pageSize
            });
            if (opts.sortName) {
                $.extend(param, {
                    sort: opts.sortName,
                    order: opts.sortOrder
                });
            }
            if (opts.onBeforeLoad.call(target, param) == false) return;

            $(target).datagrid('loading');
            var result = opts.loader.call(target, param, function (data) {
                $(target).datagrid('loaded');
                var data = opts.loadFilter.call(target, data);
                opts.onFetch.call(target, page, data.rows);
                if (data.rows && data.rows.length) {
                    callback.call(opts.view, data.rows);
                } else {
                    opts.onLoadSuccess.call(target, data);
                }
            }, function () {
                $(target).datagrid('loaded');
                opts.onLoadError.apply(target, arguments);
            });
            if (result == false) {
                $(target).datagrid('loaded');
                if (!state.data.firstRows.length) {
                    opts.onFetch.call(target, page, state.data.firstRows);
                    opts.onLoadSuccess.call(target, state.data);
                }
            }
        }
    },

    populate: function (target) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var dc = state.dc;
        var rowHeight = opts.rowHeight;
        var maxHeight = opts.maxDivHeight;

        if (this.rows.length) {
            opts.view.render.call(opts.view, target, dc.body2, false);
            opts.view.render.call(opts.view, target, dc.body1, true);

            var body = dc.body1.add(dc.body2);
            var topDiv = body.children('div.datagrid-btable-top');
            var bottomDiv = body.children('div.datagrid-btable-bottom');
            var topHeight = this.index * rowHeight;
            var bottomHeight = state.data.total * rowHeight - this.rows.length * rowHeight - topHeight;
            fillHeight(topDiv, topHeight);
            fillHeight(bottomDiv, bottomHeight);

            state.data.rows = this.rows;

            var spos = dc.body2.scrollTop() + opts.deltaTopHeight;
            if (topHeight > opts.maxVisibleHeight) {
                opts.deltaTopHeight = topHeight - opts.maxVisibleHeight;
                fillHeight(topDiv, topHeight - opts.deltaTopHeight);
            } else {
                opts.deltaTopHeight = 0;
            }
            if (bottomHeight > opts.maxVisibleHeight) {
                fillHeight(bottomDiv, opts.maxVisibleHeight);
            } else if (bottomHeight == 0) {
                var lastCount = state.data.total % opts.pageSize;
                if (lastCount) {
                    fillHeight(bottomDiv, dc.body2.height() - lastCount * rowHeight);
                }
            }

            $(target).datagrid('setSelectionState');
            dc.body2.scrollTop(spos - opts.deltaTopHeight);

            opts.pageNumber = this.page;
            var pager = $(target).datagrid('getPager');
            if (pager.length) {
                var popts = pager.pagination('options');
                var displayMsg = popts.displayMsg;
                var msg = displayMsg.replace(/{from}/, this.index + 1);
                msg = msg.replace(/{to}/, this.index + this.rows.length);
                pager.pagination('refresh', {
                    pageNumber: this.page,
                    displayMsg: msg
                });
                popts.displayMsg = displayMsg;
            }

            opts.onLoadSuccess.call(target, {
                total: state.data.total,
                rows: this.rows
            });
        }

        function fillHeight(div, height) {
            var count = Math.floor(height / maxHeight);
            var leftHeight = height - maxHeight * count;
            if (height < 0) {
                leftHeight = 0;
            }
            var cc = [];
            for (var i = 0; i < count; i++) {
                cc.push('<div style="height:' + maxHeight + 'px"></div>');
            }
            cc.push('<div style="height:' + leftHeight + 'px"></div>');
            $(div).html(cc.join(''));
        }
    },

    updateRow: function (target, rowIndex, row) {
        var opts = $.data(target, 'datagrid').options;
        var rows = $(target).datagrid('getRows');
        var rowData = opts.finder.getRow(target, rowIndex);

        var oldStyle = _getRowStyle(rowIndex);
        $.extend(rowData, row);
        var newStyle = _getRowStyle(rowIndex);
        var oldClassValue = oldStyle.c;
        var styleValue = newStyle.s;
        var classValue = 'datagrid-row ' + (rowIndex % 2 && opts.striped ? 'datagrid-row-alt ' : ' ') + newStyle.c;

        function _getRowStyle(rowIndex) {
            var css = opts.rowStyler ? opts.rowStyler.call(target, rowIndex, rowData) : '';
            var classValue = '';
            var styleValue = '';
            if (typeof css == 'string') {
                styleValue = css;
            } else if (css) {
                classValue = css['class'] || '';
                styleValue = css['style'] || '';
            }
            return {c: classValue, s: styleValue};
        }

        function _update(frozen) {
            var fields = $(target).datagrid('getColumnFields', frozen);
            var tr = opts.finder.getTr(target, rowIndex, 'body', (frozen ? 1 : 2));
            var checked = tr.find('div.datagrid-cell-check input[type=checkbox]').is(':checked');
            tr.html(this.renderRow.call(this, target, fields, frozen, rowIndex, rowData));
            tr.attr('style', styleValue).removeClass(oldClassValue).addClass(classValue);
            if (checked) {
                tr.find('div.datagrid-cell-check input[type=checkbox]')._propAttr('checked', true);
            }
        }

        _update.call(this, true);
        _update.call(this, false);
        $(target).datagrid('fixRowHeight', rowIndex);
    },

    // insertRow: function(target, index, row){
    // 	var state = $.data(target, 'datagrid');
    // 	var data = state.data;

    // 	if (index == undefined || index == null) index = data.rows.length;
    // 	if (index > data.rows.length) index = data.rows.length;
    // 	$.fn.datagrid.defaults.view.insertRow.call(this, target, index, row);
    // 	if (data.firstRows && index <= data.firstRows.length){
    // 		data.firstRows.splice(index, 0, row);
    // 	}
    // },
    insertRow: function (target, index, row) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var data = state.data;

        var total = $(target).datagrid('getData').total;
        if (index == null) {
            index = total;
        }
        if (index > total) {
            index = total;
        }
        if (data.firstRows && index <= data.firstRows.length) {
            data.firstRows.splice(index, 0, row);
        }
        data.total++;

        var rows = this.r1.concat(this.r2);
        if (index < this.index) {
            this.reload.call(this, target);
        } else if (index <= this.index + rows.length) {
            rows.splice(index - this.index, 0, row);
            this.r1 = rows.splice(0, opts.pageSize);
            if (this.r2.length) {
                this.r2 = rows.splice(0, opts.pageSize);
            }
            this.rows = this.r1.concat(this.r2);
            this.populate.call(this, target);
            state.dc.body2.triggerHandler('scroll.datagrid');
        }
    },

    // deleteRow: function(target, index){
    // 	var data = $(target).datagrid('getData');
    // 	$.fn.datagrid.defaults.view.deleteRow.call(this, target, index);
    // 	if (data.firstRows){
    // 		data.firstRows.splice(index, 1);
    // 	}
    // },
    deleteRow: function (target, index) {
        var state = $.data(target, 'datagrid');
        var data = state.data;
        var opts = state.options;
        if (data.firstRows) {
            data.firstRows.splice(index, 1);
        }
        data.total--;

        var rows = this.r1.concat(this.r2);
        if (index < this.index) {
            this.reload.call(this, target);
        } else if (index < this.index + rows.length) {
            rows.splice(index - this.index, 1);
            this.r1 = rows.splice(0, opts.pageSize);
            if (this.r1.length < opts.pageSize) {
                this.reload.call(this, target);
            } else {
                this.r2 = [];
                this.rows = this.r1.concat(this.r2);
                this.populate.call(this, target);
                state.dc.body2.triggerHandler('scroll.datagrid');
            }
        }
    }
});

$.fn.datagrid.methods.baseGetRowIndex = $.fn.datagrid.methods.getRowIndex;
$.fn.datagrid.methods.baseScrollTo = $.fn.datagrid.methods.scrollTo;
$.fn.datagrid.methods.baseGotoPage = $.fn.datagrid.methods.gotoPage;
$.extend($.fn.datagrid.methods, {
    getRowIndex: function (jq, id) {
        var opts = jq.datagrid('options');
        if (opts.view.type == 'scrollview') {
            // return jq.datagrid('baseGetRowIndex', id) + opts.view.index;
            var index = jq.datagrid('baseGetRowIndex', id);
            if (index == -1) {
                return -1;
            } else {
                return index + opts.view.index;
            }
        } else {
            return jq.datagrid('baseGetRowIndex', id);
        }
    },
    getRow: function (jq, index) {
        return jq.datagrid('options').finder.getRow(jq[0], index);
    },
    gotoPage: function (jq, param) {
        return jq.each(function () {
            var target = this;
            var opts = $(target).datagrid('options');
            if (opts.view.type == 'scrollview') {
                var page, callback;
                if (typeof param == 'object') {
                    page = param.page;
                    callback = param.callback;
                } else {
                    page = param;
                }
                opts.view.getRows.call(opts.view, target, page, function (rows) {
                    this.page = page;
                    this.index = (page - 1) * opts.pageSize;
                    this.rows = rows;
                    this.r1 = rows;
                    this.r2 = [];
                    this.populate.call(this, target);
                    $(target).data('datagrid').dc.body2.scrollTop(this.index * opts.rowHeight - opts.deltaTopHeight);
                    if (callback) {
                        callback.call(target, page);
                    }
                });
            } else {
                $(target).datagrid('baseGotoPage', param);
            }
        });
    },
    scrollTo: function (jq, param) {
        return jq.each(function () {
            var target = this;
            var opts = $(target).datagrid('options');
            var index, callback;
            if (typeof param == 'object') {
                index = param.index;
                callback = param.callback;
            } else {
                index = param;
            }
            var view = opts.view;
            if (view.type == 'scrollview') {
                if (index >= view.index && index < view.index + view.rows.length) {
                    $(target).datagrid('baseScrollTo', index);
                    if (callback) {
                        callback.call(target, index);
                    }
                } else if (index >= 0) {
                    var page = Math.floor(index / opts.pageSize) + 1;
                    $(target).datagrid('gotoPage', {
                        page: page,
                        callback: function () {
                            setTimeout(function () {
                                $(target).datagrid('baseScrollTo', index);
                                if (callback) {
                                    callback.call(target, index);
                                }
                            }, 0);
                        }
                    });
                }
            } else {
                $(target).datagrid('baseScrollTo', index);
                if (callback) {
                    callback.call(target, index);
                }
            }
        });
    }
});

$.extend($.fn.datagrid.methods, {
    fixDetailRowHeight: function (jq, index) {
        return jq.each(function () {
            var opts = $.data(this, 'datagrid').options;
            var dc = $.data(this, 'datagrid').dc;
            var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
            var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
            // fix the detail row height
            if (tr2.is(':visible')) {
                tr1.css('height', '');
                tr2.css('height', '');
                var height = Math.max(tr1.height(), tr2.height());
                tr1.css('height', height);
                tr2.css('height', height);
            }
            dc.body2.triggerHandler('scroll');
        });
    },
    getExpander: function (jq, index) {	// get row expander object
        var opts = $.data(jq[0], 'datagrid').options;
        return opts.finder.getTr(jq[0], index).find('span.datagrid-row-expander');
    },
    // get row detail container
    getRowDetail: function (jq, index) {
        var opts = $.data(jq[0], 'datagrid').options;
        var tr = opts.finder.getTr(jq[0], index, 'body', 2);
        return tr.next().find('div.datagrid-row-detail');
    },
    expandRow: function (jq, index) {
        return jq.each(function () {
            var opts = $(this).datagrid('options');
            var dc = $.data(this, 'datagrid').dc;
            var expander = $(this).datagrid('getExpander', index);
            if (expander.hasClass('datagrid-row-expand')) {
                expander.removeClass('datagrid-row-expand').addClass('datagrid-row-collapse');
                var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
                var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
                tr1.show();
                tr2.show();
                $(this).datagrid('fixDetailRowHeight', index);
                if (opts.onExpandRow) {
                    var row = $(this).datagrid('getRows')[index];
                    opts.onExpandRow.call(this, index, row);
                }
            }
        });
    },
    collapseRow: function (jq, index) {
        return jq.each(function () {
            var opts = $(this).datagrid('options');
            var dc = $.data(this, 'datagrid').dc;
            var expander = $(this).datagrid('getExpander', index);
            if (expander.hasClass('datagrid-row-collapse')) {
                expander.removeClass('datagrid-row-collapse').addClass('datagrid-row-expand');
                var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
                var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
                tr1.hide();
                tr2.hide();
                dc.body2.triggerHandler('scroll');
                if (opts.onCollapseRow) {
                    var row = $(this).datagrid('getRows')[index];
                    opts.onCollapseRow.call(this, index, row);
                }
            }
        });
    }
});
